package de.hijacksoft.isle.adapters;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;
import de.hijacksoft.isle.classes.Item;

/**
 * This class is used to retrieve information from the Barcoo database using barcodes
 * @author HiJack Soft
 */
public class BarcooDBAdapter {

    // Constants
    private static final String TAG = "BarcooDBAdapter";
    /**
     * This will be set as id if no item was found
     */
    public static final int NOTFOUNDID = -666;
    /**
     * This represents the progress of the download
     */
    private static double progress = 0;
    private static ArrayList<ProgressListener> listeners = new ArrayList<ProgressListener>();

    /**
     * Add a progess listener to the adapter
     * @param pl the listener to be added
     */
    public static void addProgressListener(ProgressListener pl) {
        listeners.add(pl);
    }

    /**
     * Remove a progess listener from the adapter
     * @param pl the listener to be removed
     */
    public static void removeProgressListener(ProgressListener pl) {
        listeners.remove(pl);
    }

    /**
     * This method will try to fetch an item from barcoo.com using a provided barcode
     * @param barcode The barcode of the item
     * @return returns an Item if an Item with an id other than NOTFOUNDID if an item was found, returns null or an Item with id = NOTFOUNDID if no item was found or an error occured.
     */
    public static Item getItem(long barcode) {
        return getItem("" + barcode);
    }

    private static void notifyListeners() {
        for (ProgressListener listener : listeners) {
            listener.onProgressUpdate((int) progress);
        }
    }

    /**
     * This method will try to fetch an item from barcoo.com using a provided barcode
     * @param barcode The barcode of the item
     * @return returns an Item if an Item with an id other than NOTFOUNDID if an item was found, returns null or an Item with id = NOTFOUNDID if no item was found or an error occured.
     */
    public static Item getItem(String barcode) {

        // RETURNS:
        // INTERNET FAIL: NULL
        // NOT FOUND    : FAILITEM (NOTFOUNDID)
        // OK           : ITEM

        // reset the progress
        progress = 0;

        // start try-catch block to prevent possible crashing at connection loss
        try {

            // set up the input stream using the barcoo.com/de url
            // there is no difference in /de, /en or /fr. All items are reachable from /de
            URL url = new URL("http://www.barcoo.com/de/" + barcode);
            InputStream is = url.openStream();
            DataInputStream dis;

            dis = new DataInputStream(new BufferedInputStream(is));

            // set the webpage string to null
            String webpage = "";

            int maxlines = 54;     // that much lines are more than enough
            int lines = 0;         // a counter that counts the number of lines donwloaded

            Item item = new Item();
            item.barcode = Long.parseLong(barcode);

            boolean found = false;

            // regex pattern to "scan" the downloaded string for price and name
            // search webpage for the price
            Pattern pricePattern = Pattern.compile("</span> \\d+,\\d+ <span class=\"cur\">");
            // search webpage for name
            Pattern namePattern = Pattern.compile("<div class=\"prodName\">.*</div>");

            Matcher m;

            String s = null;
            // start a loop that continues until price and name are found or the maximum number of lines is read
            while ((s = dis.readLine()) != null && lines < 2 * maxlines && !found) {  // double maxlines just to be sure
                webpage += s + "\n";
                lines++;
                progress = ((double) lines / (double) maxlines) * (double) 100;
                notifyListeners();
                Log.i(TAG, "downloading ..." + (progress + "%").replaceAll((progress + "%").replaceFirst("\\d*.\\d\\d?", ""), "") + "%");

                // set the matcher to match the name pattern
                m = namePattern.matcher(webpage);

                if (m.find()) {

                    // set the name to the found name ... after replacing some strings
                    item.name = germanize(m.group().replace("<div class=\"prodName\">", "").replaceAll("</div>", ""));
                    // set the matcher to match the price pattern
                    m = pricePattern.matcher(webpage);

                    if (m.find()) {
                        // set the price to the found price ... after replacing some strings
                        item.price = Double.parseDouble(m.group().replace("</span>", "").replaceAll("<span class=\"cur\">", "").replaceAll("\\s+", "").replace(",", "."));
                        found = true;
                    }

                }

            }

            // break if no webpage could be downloaded and return null
            if (webpage.equals("")) {
                return null;
            }

            // try closing the input stream to prevent the app from crashing in case the input stream could not be closed (never opened)
            try {
                is.close();
            } catch (IOException ioe) {
                Log.d(TAG, "unable to close input stream:" + ioe.getMessage());
            }

            // return the item if an item was found
            if (item.name != null && !item.name.equals("null")) {
                return item;
            } // if no item was found return an item with the NOTFOUNDID
            else {
                Item notfound = new Item();
                notfound.id = NOTFOUNDID;
                return notfound;
            }

        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
            return null;
        }

    }

    /**
     * Get the current progress of the download
     * @return returns the progress
     */
    public static double getProgress() {
        return progress;
    }

    /**
     * @author H-net @ http://www.java-forum.org/allgemeine-java-themen/82472-umlaute-zurueckwandeln.html
     * @param str a given strange looking String
     * @return the unicode version of the String
     */
    private static String germanize(String str) {
        String[] quelle = {"Ãœ", "Ã¼", "Ã¶", "Ã¤", "Ã„", "ÃŸ", "Ã©", "Ã¨", "Ã³", "Ã±", "â€™", "Ã£", "Â´", "Ãª´", "Ã§´", "Ã‡´", "â€ž", "â€"};
        String[] ziel = {"Ü", "ü", "ö", "ä", "Ä", "ß", "é", "è", "ó", "ñ", "\'", "ã", "\'", "ê", "ç", "Ç", "„", "”"};
        for (int x = 0; x < quelle.length; x++) {
            str = str.replace(quelle[x], ziel[x]);
        }
        return str;
    }
}
